package com.hrp.analyzer.request;

import com.hrp.analyzer.core.model.Params;
import com.hrp.analyzer.model.Verifiable;
import com.hrp.analyzer.request.proxy.HttpProxy;
import com.hrp.analyzer.util.CollectionUtils;
import com.hrp.analyzer.util.SerializationUtils;
import com.hrp.analyzer.util.StringUtils;
import com.hrp.analyzer.util.uri.UrlUtils;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

/**
 * 请求数据
 *
 * @author hrp
 * @date 2020/12/20 5:51 下午
 */

public class RequestParams implements Verifiable, Serializable {
    public static final String REFERER = "Referer";
    public static final String AUTO_REFERER = "auto";
    public static final String COOKIE = "Cookie";
    public static final String USER_AGENT = "User-Agent";
    public static final String USER_AGENT_DEFAULT_VALUE = "Mozilla/5.0 (Macintosh; Intel Mac OS X 11_0_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36";
    public static final String HEADER_SEPARATOR = ": ";
    private static final long serialVersionUID = 5277637191594804618L;
    /**
     * 请求链接
     */
    private String url;
    /**
     * 请求方法
     */
    private String method;
    /**
     * 网页编码
     */
    private String charset;
    /**
     * 请求头
     */
    private Map<String, String> headers;
    /**
     * 请求方式
     */
    private String mediaType;
    /**
     * 请求体
     */
    private String body;
    /**
     * 是否为动态网页
     */
    private Boolean dynamic;
    /**
     * HTTP代理信息
     */
    private HttpProxy proxy;
    /**
     * 启用HTTP代理 标记允许代理 如果开发了全局代理配置这个字段将会自动设置为true
     */
    private Boolean enableProxy;
    /**
     * 自动代理
     * 请在全局代理中开启自动代理配置 并且设置此字段为true 才会真正启用全局代理
     * 这么做是为了方便全局控制代理的热拔插
     * 优先级 先判断此此字段为true 再判断全局AnalyzerManager.autoProxy是否开启
     */
    private Boolean autoProxy;
    /**
     * 处理脚本，在请求结束后（比如webview中执行）
     */
    private String script;
    /**
     * 动态网页延迟时间
     */
    private Long dynamicDelayTime = 500L;

    /**
     * 默认请求配置
     *
     * @param url url
     * @return /
     */
    public static RequestParams create(String url) {
        return builder().url(url).build();
    }


    public static Builder builder() {
        return new Builder();
    }

    private RequestParams() {

    }

    private RequestParams(Builder builder) {
        this.url = builder.url;
        this.method = builder.method;
        this.charset = builder.charset;
        this.headers = builder.headers;
        this.mediaType = builder.mediaType;
        this.body = builder.body;
        this.dynamic = builder.dynamic;
        this.proxy = builder.proxy;
        this.enableProxy = builder.enableProxy;
        this.autoProxy = builder.autoProxy;
        this.script = builder.script;
        this.dynamicDelayTime = builder.dynamicDelayTime;
    }


    /**
     * 创建一个请求
     *
     * @param url    网页链接
     * @param params 请求参数
     * @return RequestParams
     */
    public static RequestParams create(String url, RequestParams params) {
        if (params == null) {
            params = new RequestParams();
        } else {
            params = params.copy();
        }
        params.setUrl(url);
        return params;
    }

    public String getMethod() {
        return StringUtils.isNotEmpty(method) ? method.toUpperCase() : HttpMethod.GET.name();
    }

    /**
     * 是否启用代理
     *
     * @return true则启用
     */
    public boolean isEnableProxy() {
        if (this.proxy == null || this.proxy == HttpProxy.NO_PROXY) {
            return false;
        }
        return Boolean.TRUE.equals(enableProxy);
    }

    /**
     * 获取MediaType,非GET默认为FORM
     *
     * @return mediaType
     */
    public String getMediaType() {
        if (StringUtils.isNotBlank(mediaType)) {
            if (MediaType.FORM.name().equalsIgnoreCase(mediaType)) {
                return MediaType.FORM.getMediaType();
            }
            if (MediaType.JSON.name().equalsIgnoreCase(mediaType)) {
                return MediaType.JSON.getMediaType();
            }
        }
        return mediaType;
    }

    /**
     * 复制一份
     *
     * @return /
     */
    public RequestParams copy() {
        return SerializationUtils.deepClone(this);
    }

    /**
     * 获取请求头
     *
     * @param headerName  名称
     * @param defaultName 默认值
     * @return 请求头
     */
    public String getHeader(String headerName, String defaultName) {
        if (CollectionUtils.isNotEmpty(headers)) {
            return headers.getOrDefault(headerName, defaultName);
        }
        return defaultName;
    }

    /**
     * 获取请求头
     *
     * @param headerName 名称
     * @return 请求头
     */
    public String getHeader(String headerName) {
        if (CollectionUtils.isNotEmpty(headers)) {
            return headers.get(headerName);
        }
        return null;
    }

    /**
     * 设置请求头 存在则覆盖
     *
     * @param headerName  名称
     * @param headerValue 值
     */
    public void setHeader(String headerName, String headerValue) {
        if (headers == null) {
            headers = new HashMap<>(8);
        }
        headers.put(headerName, headerValue);
    }

    /**
     * 设置请求头 如果不存在则设置 存在则忽略
     *
     * @param headerName  名称
     * @param headerValue 值
     */
    public void addHeader(String headerName, String headerValue) {
        if (headers == null) {
            headers = new HashMap<>(8);
            headers.put(headerName, headerValue);
        } else {
            headers.putIfAbsent(headerName, headerValue);
        }
    }

    /**
     * 获取请求头字符串
     *
     * @return key: value\n ...
     */
    public String getHeaderString() {
        if (headers == null) {
            return null;
        }
        StringBuilder headersStr = new StringBuilder();
        this.headers.forEach((key, value) -> headersStr.append(key).append(HEADER_SEPARATOR).append(value).append(StringUtils.LF));
        return headersStr.toString();
    }

    /**
     * <pre>
     *   key: value\n ...
     *   key: value\n ...
     * </pre>
     * <p>
     * 设置请求头字符串
     */
    public void setHeaderString(String headersString) {
        if (StringUtils.isBlank(headersString) && headers != null) {
            headers.clear();
            return;
        }
        String[] headerLines = headersString.split(StringUtils.LF);
        // 校验合法性
        for (String headerLine : headerLines) {
            if (!headerLine.contains(HEADER_SEPARATOR) || headerLine.endsWith(HEADER_SEPARATOR)) {
                return;
            }
        }
        headers = new HashMap<>(16);
        for (String headerLine : headerLines) {
            String[] split = headerLine.split(HEADER_SEPARATOR);
            headers.put(split[0], split[1]);
        }
    }

    /**
     * 覆盖默认参数
     *
     * @param params 参数
     */
    public void overrideParams(Params params) {
        if (params == null) {
            return;
        }
        // cookie
        if (StringUtils.isNotBlank(params.getCookie()) && getHeader(RequestParams.COOKIE) == null) {
            setHeader(RequestParams.COOKIE, params.getCookie());
        }
        // ua
        if (StringUtils.isNotBlank(params.getUserAgent()) && getHeader(RequestParams.USER_AGENT) == null) {
            setHeader(RequestParams.USER_AGENT, params.getUserAgent());
        }
        // 动态网页
        if (Boolean.TRUE.equals(params.getDynamic())) {
            setDynamic(true);
        }
        // 代理
        if (Boolean.TRUE.equals(params.getAutoReferer())) {
            addHeader(RequestParams.REFERER, AUTO_REFERER);
        }
        // 代理
        if (Boolean.TRUE.equals(params.getEnabledProxy())) {
            setEnableProxy(true);
        }
    }

    @Override
    public boolean isEffective() {
        return UrlUtils.isHttpUrl(url);
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public void setMethod(String method) {
        this.method = method;
    }

    public String getCharset() {
        return charset;
    }

    public void setCharset(String charset) {
        this.charset = charset;
    }

    public Map<String, String> getHeaders() {
        return headers;
    }

    public void setHeaders(Map<String, String> headers) {
        this.headers = headers;
    }

    public void setMediaType(String mediaType) {
        this.mediaType = mediaType;
    }

    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body;
    }

    public Boolean getDynamic() {
        return dynamic;
    }

    public void setDynamic(Boolean dynamic) {
        this.dynamic = dynamic;
    }

    public HttpProxy getProxy() {
        return proxy;
    }

    public void setProxy(HttpProxy proxy) {
        this.proxy = proxy;
    }

    public Boolean getEnableProxy() {
        return enableProxy;
    }

    public void setEnableProxy(Boolean enableProxy) {
        this.enableProxy = enableProxy;
    }

    public Boolean getAutoProxy() {
        return autoProxy;
    }

    public void setAutoProxy(Boolean autoProxy) {
        this.autoProxy = autoProxy;
    }

    public String getScript() {
        return script;
    }

    public void setScript(String script) {
        this.script = script;
    }

    public Long getDynamicDelayTime() {
        return dynamicDelayTime;
    }

    public void setDynamicDelayTime(Long dynamicDelayTime) {
        this.dynamicDelayTime = dynamicDelayTime;
    }


    public static class Builder {

        /**
         * 请求链接
         */
        private String url;
        /**
         * 请求方法
         */
        private String method;
        /**
         * 网页编码
         */
        private String charset;
        /**
         * 请求头
         */
        private Map<String, String> headers;
        /**
         * 请求方式
         */
        private String mediaType;
        /**
         * 请求体
         */
        private String body;
        /**
         * 是否为动态网页
         */
        private Boolean dynamic;
        /**
         * HTTP代理信息
         */
        private HttpProxy proxy;
        /**
         * 启用HTTP代理 标记允许代理 如果开发了全局代理配置这个字段将会自动设置为true
         */
        private Boolean enableProxy;
        /**
         * 自动代理
         * 请在全局代理中开启自动代理配置 并且设置此字段为true 才会真正启用全局代理
         * 这么做是为了方便全局控制代理的热拔插
         * 优先级 先判断此此字段为true 再判断全局AnalyzerManager.autoProxy是否开启
         */
        private Boolean autoProxy;
        /**
         * 处理脚本，在请求结束后（比如webview中执行）
         */
        private String script;
        /**
         * 动态网页延迟时间
         */
        private Long dynamicDelayTime = 500L;

        public Builder url(String url) {
            this.url = url;
            return this;
        }

        public Builder method(String method) {
            this.method = method;
            return this;
        }

        public Builder charset(String charset) {
            this.charset = charset;
            return this;
        }

        public Builder headers(Map<String, String> headers) {
            this.headers = headers;
            return this;
        }

        public Builder mediaType(String mediaType) {
            this.mediaType = mediaType;
            return this;
        }

        public Builder body(String body) {
            this.body = body;
            return this;
        }

        public Builder dynamic(Boolean dynamic) {
            this.dynamic = dynamic;
            return this;
        }

        public Builder proxy(HttpProxy proxy) {
            this.proxy = proxy;
            return this;
        }

        public Builder enableProxy(Boolean enableProxy) {
            this.enableProxy = enableProxy;
            return this;
        }

        public Builder autoProxy(Boolean autoProxy) {
            this.autoProxy = autoProxy;
            return this;
        }

        public Builder script(String script) {
            this.script = script;
            return this;
        }

        public Builder dynamicDelayTime(Long dynamicDelayTime) {
            this.dynamicDelayTime = dynamicDelayTime;
            return this;
        }

        public RequestParams build() {
            return new RequestParams(this);
        }
    }

}
